home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Tool Chest / Text / WASTE / WASTE 1.2a2 / WESelecting.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-10-12  |  39.9 KB  |  1,578 lines  |  [TEXT/CWIE]

  1. /*
  2.  *    WESelecting.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  Drawing Selections, Activating, Updating, Scrolling, etc.
  6.  *
  7.  *  Copyright (c) 1993-1995 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14.  
  15. #include "WASTEIntf.h"
  16.  
  17. #ifndef __QDOFFSCREEN__
  18. #include <QDOffscreen.h>
  19. #endif
  20.  
  21. // values for _WEArrowOffset action parameter:
  22. // plain arrow keys
  23. #define kGoLeft            0
  24. #define kGoRight        1
  25. #define kGoUp            2
  26. #define kGoDown            3
  27.  
  28. // modifiers
  29. #define kOption            4
  30. #define kCommand        8
  31.  
  32. // option + arrow combos
  33. #define kGoWordStart    kGoLeft + kOption
  34. #define kGoWordEnd        kGoRight + kOption
  35. #define kGoTextStart    kGoUp + kOption
  36. #define kGoTextEnd        kGoDown + kOption
  37.  
  38. // command + arrow combos
  39. #define kGoLineStart    kGoLeft + kCommand
  40. #define kGoLineEnd        kGoRight + kCommand
  41. #define kGoPageStart    kGoUp + kCommand
  42. #define kGoPageEnd        kGoDown + kCommand
  43.  
  44.  
  45. /*inline*/ pascal void _WEClearHiliteBit(void)
  46. {
  47.     LMSetHiliteMode(LMGetHiliteMode() & 0x7f);
  48. }
  49.  
  50. static Boolean SLPixelToChar(LinePtr pLine, const WERunAttributes *pAttrs,
  51.         Ptr pSegment, long segmentStart, long segmentLength,
  52.         JustStyleCode styleRunPosition, void *callbackData);
  53.  
  54. static Boolean SLPixelToChar(LinePtr pLine, const WERunAttributes *pAttrs,
  55.         Ptr pSegment, long segmentStart, long segmentLength,
  56.         JustStyleCode styleRunPosition, void *callbackData)
  57. {
  58.     struct SLPixelToCharData *p = (struct SLPixelToCharData *) callbackData;
  59.     WEPtr pWE = *p->hWE;
  60.     Fixed slop;
  61.     short cType;
  62.     Fixed oldWidth;
  63. #if WASTE_OBJECTS
  64.     Fixed objectWidth;
  65.     Fixed subWidth;
  66. #endif
  67.  
  68.     // if this is the first style run on the line, subtract pen indent from pixelWidth
  69.     if (IS_FIRST_RUN(styleRunPosition))
  70.     {
  71.         p->pixelWidth -= BSL(_WECalcPenIndent(pLine->lineSlop, pWE->alignment), 16);
  72.     }
  73.  
  74.     // if pixelWidth is gone negative already, the point is on the trailing edge of first glyph
  75.     if (p->pixelWidth < 0)
  76.     {
  77.         p->offset = segmentStart;
  78.         *p->edge = kTrailingEdge;
  79.         return true;    // stop looping
  80.     }
  81.     
  82.     oldWidth = p->pixelWidth;
  83.  
  84. #if WASTE_OBJECTS
  85.     if (pAttrs->runStyle.tsObject != NULL)
  86.     {
  87.  
  88.         // EMBEDDED OBJECT
  89.         // calculate object width as Fixed
  90.         objectWidth = BSL((*pAttrs->runStyle.tsObject)->objectSize.h, 16);
  91.  
  92.         // subtract object width from pixelWidth
  93.         p->pixelWidth -= objectWidth;
  94.  
  95. #if WASTE_OBJECTS_ARE_GLYPHS
  96.  
  97.         // find out whether the point is in the leftmost half of the object,
  98.         // in the rightmost half or past the object
  99.         subWidth = objectWidth >> 1;    // divide by two
  100.         if (p->pixelWidth + subWidth < 0)
  101.         {
  102.             p->offset = segmentStart;
  103.             *p->edge = kLeadingEdge;        // point is in leftmost half of object
  104.         }
  105.         else
  106.         {
  107.             p->offset= segmentStart + 1;
  108.             if (p->pixelWidth < 0)
  109.                 *p->edge = kTrailingEdge;    // point is in rightmost half of object
  110.             else
  111.                 *p->edge = kLeadingEdge;    // point is past object
  112.         }
  113. #else
  114.  
  115.         // find out whether the point is in the leftmost quarter of the object,
  116.         // in the middle half, in the rightmost quarter or past the object
  117.         subWidth = objectWidth >> 2;    // divide by four
  118.         if (p->pixelWidth + subWidth < 0)
  119.         {
  120.             p->offset = segmentStart;
  121.             if (p->pixelWidth + objectWidth < subWidth)
  122.                 *p->edge = kLeadingEdge;        // point is in leftmost quarter of object
  123.             else
  124.                 *p->edge = kObjectEdge;        // point is in middle half of object
  125.         }
  126.         else
  127.         {
  128.             p->offset = segmentStart + 1;
  129.             if (p->pixelWidth < 0)
  130.                 *p->edge = kTrailingEdge;    // point is in rightmost quarter of object
  131.             else
  132.                 *p->edge = kLeadingEdge;        // point is past object
  133.         }
  134. #endif // WASTE_OBJECTS_ARE_GLYPHS
  135.     }
  136.     else
  137. #endif // WASTE_OBJECTS
  138.     {
  139.     
  140.         // REGULAR TEXT
  141.     
  142.         // if this is the last segment on the line, strip the last blank character (if any),
  143.         // unless it is the last non-CR character in the whole text stream
  144.         if (IS_LAST_RUN(styleRunPosition))
  145.         {
  146.             if ((segmentStart + segmentLength < pWE->textLength) ||
  147.                 pSegment[segmentLength - 1] == kEOL)
  148.             {
  149.                 cType = CallWECharTypeProc(pSegment, segmentLength - 1, FontScript(),
  150.                      p->hWE, pWE->charTypeHook);
  151.                 if ((cType & (smcTypeMask + smcClassMask)) == smCharPunct + smPunctBlank)
  152.                 {
  153.                     segmentLength -= ((cType & smcDoubleMask) ? 2 : 1);
  154.                 }
  155.             }
  156.         }
  157.     
  158.         // calculate slop for this text segment (justified text only)
  159.         if (pWE->alignment == weJustify) 
  160.         {
  161.             slop = FixMul(PortionLine(pSegment, segmentLength, styleRunPosition,
  162.                 kOneToOneScaling, kOneToOneScaling), pLine->lineJustAmount);
  163.         }
  164.         else
  165.         {
  166.             slop = 0;
  167.         }
  168.     
  169.         // call PixelToChar hook for this segment
  170.         p->offset = segmentStart + CallWEPixelToCharProc(pSegment,
  171.                 segmentLength, slop, &p->pixelWidth, p->edge, styleRunPosition, p->hPos,
  172.                 p->hWE, pWE->pixelToCharHook);
  173.     }
  174.     
  175.     // increment hPos by change in pixelWidth
  176.     p->hPos += (oldWidth - p->pixelWidth);
  177.  
  178.     // if pixelWidth has gone negative, we're finished; otherwise go to next run
  179.     return (p->pixelWidth < 0);
  180. }
  181.  
  182. pascal long WEGetOffset(const LongPt *thePoint, char *edge, WEHandle hWE)
  183. {
  184.     // given a long point in local coordinates,
  185.     // find the text offset corresponding to the nearest glyph
  186.  
  187.     WEPtr pWE;
  188.     long lineIndex;
  189.     Fixed pixelWidth;
  190.     Boolean saveWELock;
  191.     struct SLPixelToCharData callbackData;
  192.     LongPt tempPoint = *thePoint; // so we don't change original point
  193.     long offset;
  194.     char theEdge;
  195.  
  196.     // lock the WE record
  197.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  198.     pWE = *hWE;
  199.  
  200.     // offset thePoint so that it is relative to the top left corner of the destination rectangle
  201.     tempPoint.v -= pWE->destRect.top;
  202.     tempPoint.h -= pWE->destRect.left;
  203.  
  204.     // if the point is above the destination rect, return zero
  205.     if (tempPoint.v < 0) 
  206.     {
  207.         offset = 0;
  208.         theEdge = kTrailingEdge;
  209.     }
  210.     else
  211.     {
  212.         // if the point is below the last line, return last char offset
  213.         if (tempPoint.v >= WEGetHeight(0, LONG_MAX, hWE)) 
  214.         {
  215.             offset = pWE->textLength;
  216.             theEdge = kLeadingEdge;
  217.         }
  218.         else
  219.         {
  220.             // find the line index corresponding to the vertical pixel offset
  221.             lineIndex = _WEPixelToLine(tempPoint.v, hWE);
  222.  
  223.             // express the horizontal pixel offset as a Fixed value
  224.             pixelWidth = BSL(tempPoint.h, 16);
  225.  
  226.             // walk through the segments on this line calling PixelToChar
  227.             callbackData.hWE = hWE;
  228.             callbackData.hPos = 0;
  229.             callbackData.pixelWidth = pixelWidth;
  230.             callbackData.edge = &theEdge;
  231.             callbackData.offset = 0;
  232.             _WESegmentLoop(lineIndex, lineIndex, SLPixelToChar, (void *)&callbackData, hWE);
  233.             offset = callbackData.offset;
  234.         }
  235.     }
  236.     
  237.     // unlock the WE record
  238.     _WESetHandleLock((Handle) hWE, saveWELock);
  239.  
  240.     // return offset/edge pair
  241.     if (edge != NULL)
  242.         *edge = theEdge;
  243.     return offset;
  244. }
  245.  
  246. static Boolean SLCharToPixel(LinePtr pLine, const WERunAttributes *pAttrs,
  247.         Ptr pSegment, long segmentStart, long segmentLength,
  248.         JustStyleCode styleRunPosition, void *callbackData);
  249.  
  250. static Boolean SLCharToPixel(LinePtr pLine, const WERunAttributes *pAttrs,
  251.         Ptr pSegment, long segmentStart, long segmentLength,
  252.         JustStyleCode styleRunPosition, void *callbackData)
  253. {
  254.     struct SLCharToPixelData *p = (struct SLCharToPixelData *) callbackData;
  255.     WEPtr pWE = *p->hWE;
  256.     Fixed slop;
  257.     short width;
  258.     Boolean isInSegment;
  259.  
  260.     // is offset within this segment?
  261.     isInSegment = (p->offset < segmentStart + segmentLength);
  262.  
  263.     // if this is the first style run on the line, add pen indent to thePoint.h
  264.     if (IS_FIRST_RUN(styleRunPosition))
  265.     {
  266.         p->thePoint->h += _WECalcPenIndent(pLine->lineSlop, pWE->alignment);
  267.     }
  268.  
  269. #if WASTE_OBJECTS
  270.     if (pAttrs->runStyle.tsObject != NULL)
  271.     {
  272.     
  273.         // EMBEDDED OBJECT
  274.         width = isInSegment ? 0 : (*(pAttrs->runStyle.tsObject))->objectSize.h;
  275.     }
  276.     else
  277. #endif
  278.     {
  279.         // REGULAR TEXT
  280.         // calculate slop for this text segment (justified text only)
  281.         if (pWE->alignment == weJustify) 
  282.         {
  283.             slop = FixMul(PortionLine(pSegment, segmentLength, styleRunPosition,
  284.                 kOneToOneScaling, kOneToOneScaling), pLine->lineJustAmount);
  285.         }
  286.         else
  287.         {
  288.             slop = 0;
  289.         }
  290.         
  291.         // call CharToPixel to get width of segment up to specified offset
  292.         width = CallWECharToPixelProc(pSegment, segmentLength,
  293.             slop, p->offset - segmentStart, hilite, styleRunPosition, p->thePoint->h,
  294.             p->hWE, pWE->charToPixelHook);
  295.     }
  296.     
  297.     // advance thePoint.h by the width of this segment
  298.     p->thePoint->h += width;
  299.  
  300.     // drop out of loop when we reach offset
  301.     return isInSegment;
  302. }
  303.  
  304. pascal void WEGetPoint(long offset, LongPt *thePoint, short *lineHeight, WEHandle hWE)
  305. {
  306.     // given a byte offset into the text, find the corresponding glyph position
  307.     // this routine is useful for highlighting the text and for positioning the caret
  308.  
  309.     WEPtr pWE;
  310.     LineRec *pLine;
  311.     long lineIndex;
  312.     Boolean saveWELock;
  313.     struct SLCharToPixelData callbackData;    
  314.     
  315.     // lock the WE record
  316.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  317.     pWE = *hWE;
  318.  
  319.     // the base point is the top left corner of the destination rectangle
  320.     *thePoint = * (LongPt *) &pWE->destRect;
  321.  
  322.     // first of all find the line on which the glyph lies
  323.     lineIndex = WEOffsetToLine(offset, hWE);
  324.  
  325.     // calculate the vertical coordinate and the line height
  326.     pLine = *pWE->hLines + lineIndex;
  327.     thePoint->v += pLine->lineOrigin;
  328.     *lineHeight = pLine[1].lineOrigin - pLine[0].lineOrigin;
  329.  
  330.     if ((offset == pWE->textLength) && (WEGetChar(offset - 1, hWE) == kEOL)) 
  331.     {
  332.         // SPECIAL CASE: if offset is past the last character and
  333.         // the last character is a carriage return, return a point below the last line
  334.  
  335.         thePoint->v += *lineHeight;
  336.         thePoint->h += _WECalcPenIndent(pWE->destRect.right - pWE->destRect.left, pWE->alignment);
  337.     }
  338.     else
  339.     {
  340.         callbackData.hWE = hWE;
  341.         callbackData.offset = offset;
  342.         callbackData.thePoint = thePoint;
  343.         // to get the horizontal coordinate, walk through the style runs on this line
  344.         _WESegmentLoop(lineIndex, lineIndex, SLCharToPixel, (void *)&callbackData, hWE);
  345.     }
  346.  
  347.     // pin the horizontal coordinate to the destination rectangle
  348.     thePoint->h = _WEPinInRange(thePoint->h, pWE->destRect.left, pWE->destRect.right);
  349.  
  350.     // unlock the WE record
  351.     _WESetHandleLock((Handle) hWE, saveWELock);
  352. }
  353.  
  354. pascal void WEFindLine(long offset, char edge, long *lineStart, long *lineEnd, WEHandle hWE)
  355. {
  356. #pragma unused(edge)
  357.     WEPtr pWE = *hWE;
  358.     LineRec *pLine;
  359.     
  360.     pLine = *pWE->hLines + WEOffsetToLine(offset, hWE);
  361.     *lineStart = pLine[0].lineStart;
  362.     *lineEnd = pLine[1].lineStart;
  363. }
  364.  
  365.  
  366. pascal long _WEGetLineStart(long lineNo, WEHandle hWE)
  367. {
  368.     WEPtr pWE = *hWE;
  369.  
  370.     if (lineNo >= pWE->nLines)
  371.         lineNo = pWE->nLines - 1;
  372.     return ((*pWE->hLines)[lineNo].lineStart);
  373. }
  374.  
  375.  
  376. pascal ScriptCode _WEGetContext(long offset, long *contextStart, long *contextEnd,
  377.                         WEHandle hWE)
  378. {
  379.     // This function finds a range of characters ("context"), all belonging to the same script
  380.     // and centered around the specified offset.
  381.     // The function result is the ID of a font belonging to this script.
  382.     // Ideally, the context should consist of a whole script run, but in practice the returned
  383.     // context can be narrower, for performance and other reasons (see below)
  384.  
  385.     long index, saveIndex, saveRunEnd;
  386.     WERunInfo runInfo;
  387.     ScriptCode script1, script2;
  388.     short retval;
  389.     
  390.     if (BTST((*hWE)->flags, weFNonRoman))
  391.     { 
  392.         // if more than one script is installed, limit the search of script run boundaries
  393.         // to a single line, for speed's sake
  394.         WEFindLine(offset, kLeadingEdge, contextStart, contextEnd, hWE);
  395.  
  396.         // find the style run the specified offset is in
  397.         index = _WEOffsetToRun(offset, hWE);
  398.         _WEGetIndStyle(index, &runInfo, hWE);
  399.  
  400.         // find the script code associated with this style run
  401.         script1 = FontToScript(runInfo.runAttrs.runStyle.tsFont);
  402.  
  403.         // the script code is returned as function result
  404.         retval = script1;
  405.  
  406.         // save index and runInfo.runEnd for the second while loop
  407.         saveIndex = index;
  408.         saveRunEnd = runInfo.runEnd;
  409.  
  410.         // walk backwards across style runs preceding offset, looking for a script run boundary
  411.         while (runInfo.runStart > *contextStart)
  412.         {
  413.             index--;
  414.             _WEGetIndStyle(index, &runInfo, hWE);
  415.             script2 = FontToScript(runInfo.runAttrs.runStyle.tsFont);
  416.             if (script1 != script2) 
  417.             {
  418.                 *contextStart = runInfo.runEnd;
  419.                 break;
  420.             }
  421.         }
  422.  
  423.         // restore index and runInfo.runEnd
  424.         index = saveIndex;
  425.         runInfo.runEnd = saveRunEnd;
  426.  
  427.         // walk forward across style runs following offset, looking for a script run boundary
  428.         while (runInfo.runEnd < *contextEnd)
  429.         {
  430.             index++;
  431.             _WEGetIndStyle(index, &runInfo, hWE);
  432.             script2 = FontToScript(runInfo.runAttrs.runStyle.tsFont);
  433.             if (script1 != script2) 
  434.             {
  435.                 *contextEnd = runInfo.runStart;
  436.                 break;
  437.             }
  438.         }
  439.     }
  440.     else
  441.     {
  442.         // only the Roman script is enabled: the whole text constitutes one script run
  443.         retval = smRoman;
  444.         *contextStart = 0;
  445.         *contextEnd = (*hWE)->textLength;
  446.     }
  447.  
  448.     // make sure the range identified by contextStart/contextEnd is contained within
  449.     // the 32K byte range centered around the specified offset
  450.     // the reason for this is that many Script Manager routines (e.g. FindWord and CharByte)
  451.     // only accept 16-bit offsets, rather than 32-bit offsets
  452.  
  453.     *contextStart = _WEPinInRange(*contextStart, offset - (SHRT_MAX / 2), offset);
  454.     *contextEnd = _WEPinInRange(*contextEnd, offset, offset + (SHRT_MAX / 2));
  455.  
  456.     return retval;
  457. }
  458.  
  459. pascal ScriptCode _WEGetRestrictedContext(long offset, long *contextStart, long *contextEnd,
  460.                         WEHandle hWE)
  461. {
  462.     // This function finds a range of characters ("context"), all belonging to the same script
  463.     // and centered around the specified offset.
  464.     // This function returns a script run subrange and is more efficient than
  465.     // _WEGetContext because it doesn't try to find the script boundaries accurately.
  466.  
  467.     WERunInfo runInfo;
  468.  
  469.     // just find the style run the specified offset is in
  470.     WEGetRunInfo(offset, &runInfo, hWE);
  471.     *contextStart = runInfo.runStart;
  472.     *contextEnd = runInfo.runEnd;
  473.  
  474.     // make sure the range identified by contextStart/contextEnd is contained within
  475.     // the 32K byte range centered around the specified offset
  476.     // the reason for this is that many Script Manager routines (e.g. FindWord and CharByte)
  477.     // only accept 16-bit offsets, rather than 32-bit offsets
  478.  
  479.     *contextStart = _WEPinInRange(*contextStart, offset - (SHRT_MAX / 2), offset);
  480.     *contextEnd = _WEPinInRange(*contextEnd, offset, offset + (SHRT_MAX / 2));
  481.  
  482.     return FontToScript(runInfo.runAttrs.runStyle.tsFont);
  483. } // _WEGetRestrictedContext
  484.  
  485. pascal void WEFindWord(long offset, char edge, long *wordStart, long *wordEnd, WEHandle hWE)
  486. {
  487.     WEPtr pWE;
  488.     ScriptCode script;
  489.     long contextStart, contextEnd;
  490.     Handle hText;
  491.     OffsetTable wordBreaks;
  492.     Boolean saveTextLock, saveWELock;
  493.  
  494.     // lock the WE record
  495.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  496.     pWE = *hWE;
  497.  
  498.     // find a script context containing the specified offset
  499.     // (words cannot straddle script boundaries)
  500.     script = _WEGetContext(offset, &contextStart, &contextEnd, hWE);
  501.  
  502.     // lock the text
  503.     hText = pWE->hText;
  504.     saveTextLock = _WESetHandleLock(hText, true);
  505.  
  506.     // call the word break hook
  507.     CallWEWordBreakProc(*hText + contextStart, contextEnd - contextStart,
  508.         offset - contextStart, edge, wordBreaks, script, hWE, pWE->wordBreakHook);
  509.  
  510.     // unlock the text
  511.     _WESetHandleLock(hText, saveTextLock);
  512.  
  513.     // calculate wordStart and wordEnd relative to the beginning of the text
  514.     *wordStart = contextStart + wordBreaks[0].offFirst;
  515.     *wordEnd = contextStart + wordBreaks[0].offSecond;
  516. }
  517.  
  518. pascal short WECharByte(long offset, WEHandle hWE)
  519. {
  520.     WEPtr pWE;
  521.     ScriptCode script;
  522.     long contextStart, contextEnd;
  523.     short retval;
  524.     Boolean saveWELock, saveTextLock;
  525.     Handle hText;
  526.  
  527.     retval = smSingleByte;
  528.     pWE = *hWE;
  529.  
  530.     // lock the WE record
  531.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  532.     pWE = *hWE;
  533.  
  534.     // do nothing unless there is at least one double-byte script system installed
  535.     // and make sure offset is within allowed bounds
  536.     if (BTST(pWE->flags, weFDoubleByte))
  537.     {
  538.         if ((offset >= 0) && (offset < pWE->textLength))
  539.         {
  540.  
  541.             // find a script context containing the specified offset
  542.             script = _WEGetRestrictedContext(offset, &contextStart, &contextEnd, hWE);
  543.  
  544.             // lock the text
  545.             hText = pWE->hText;
  546.             saveTextLock = _WESetHandleLock(hText, true);
  547.  
  548.             // pass the CharByte hook a pointer to the beginning of the style run
  549.             retval = CallWECharByteProc(*hText + contextStart,
  550.                 offset - contextStart, script, hWE, pWE->charByteHook);
  551.  
  552.             // unlock the text
  553.             _WESetHandleLock(hText, saveTextLock);
  554.         }
  555.     }
  556.     
  557.     // unlock the WE record
  558.     _WESetHandleLock((Handle) hWE, saveWELock);
  559.  
  560.     return retval;
  561.  
  562. } // WECharByte
  563.  
  564. pascal short WECharType(long offset, WEHandle hWE)
  565. {
  566.     WEPtr pWE;
  567.     ScriptCode script;
  568.     long contextStart, contextEnd;
  569.     Handle hText;
  570.     Boolean saveWELock, saveTextLock;
  571.     short retval;
  572.     
  573.     retval = 0;
  574.  
  575.     // lock the WE record
  576.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  577.     pWE = *hWE;
  578.  
  579.     // make sure offset is within allowed bounds
  580.     if ((offset >= 0) && (offset < pWE->textLength))
  581.     {
  582.  
  583.         // find a script context containing the specified offset
  584.         script = _WEGetRestrictedContext(offset, &contextStart, &contextEnd, hWE);
  585.  
  586.         // lock the text
  587.         hText = pWE->hText;
  588.         saveTextLock = _WESetHandleLock(hText, true);
  589.  
  590.         // pass the CharType hook a pointer to the beginning of the style run
  591.         retval = CallWECharTypeProc(*hText + contextStart,
  592.             offset - contextStart, script, hWE, pWE->charTypeHook);
  593.  
  594.         // unlock the text
  595.         _WESetHandleLock(hText, saveTextLock);
  596.     }
  597.  
  598.     // unlock the WE record
  599.     _WESetHandleLock((Handle) hWE, saveWELock);
  600.  
  601.     return retval;
  602. } // WECharType
  603.  
  604.  
  605. pascal void _WEDrawCaret(long offset, WEHandle hWE)
  606. {
  607.     WEPtr pWE = *hWE;    // assume WE record is already locked
  608.     LongPt thePoint;
  609.     Rect caretRect;
  610.     short caretHeight;
  611.     GrafPtr savePort;
  612.     RgnHandle saveClip;
  613.  
  614.     // find the caret position using WEGetPoint
  615.     WEGetPoint(offset, &thePoint, &caretHeight, hWE);
  616.     WELongPointToPoint(&thePoint, (Point *)&caretRect.top);
  617.     if (caretRect.left > pWE->destRect.left) 
  618.     {
  619.         caretRect.left--;
  620.     }
  621.     
  622.     // calculate caret rectangle
  623.     caretRect.bottom = caretRect.top + caretHeight;
  624.     caretRect.right = caretRect.left + kCaretWidth;
  625.  
  626.     // set up the port
  627.     GetPort(&savePort);
  628.     SetPort(pWE->port);
  629.  
  630.     // clip to the view region
  631.     saveClip = NewRgn();
  632.     GetClip(saveClip);
  633.     SetClip(pWE->viewRgn);
  634.  
  635.     // draw the caret
  636.     InvertRect(&caretRect);
  637.  
  638.     // restore the clip region
  639.     SetClip(saveClip);
  640.     DisposeRgn(saveClip);
  641.  
  642.     // restore the port
  643.     SetPort(savePort);
  644. }
  645.  
  646. pascal void _WEBlinkCaret(WEHandle hWE)
  647. {
  648.     WEPtr pWE = *hWE;    // assume WE record is already locked
  649.  
  650.     // do nothing if we're not active
  651.     if (!BTST(pWE->flags, weFActive))
  652.         return;
  653.  
  654. #if WASTE_NO_RO_CARET
  655.     if (BTST(pWE->features, weFReadOnly) && !BTST(pWE->flags, weFCaretVisible))
  656.         return;
  657. #endif
  658.  
  659.     // redraw the caret, in XOR mode
  660.     _WEDrawCaret(pWE->selStart, hWE);
  661.  
  662.     // keep track of the current caret visibility status
  663.     BCHG(pWE->flags, weFCaretVisible);    // invert flag
  664.  
  665.     // update caretTime
  666.     pWE->caretTime = TickCount();
  667.  
  668. } // _WEBlinkCaret
  669.  
  670. pascal RgnHandle WEGetHiliteRgn(long rangeStart, long rangeEnd, WEHandle hWE)
  671. {
  672.     // returns the hilite region corresponding to the specified range
  673.     // the caller is responsible for disposing of the returned region
  674.     // when it's finished with it
  675.  
  676.     WEPtr pWE;
  677.     RgnHandle hiliteRgn;
  678.     LongRect selRect;
  679.     LongPt firstPoint, lastPoint;
  680.     short firstLineHeight, lastLineHeight;
  681.     Rect r;
  682.     GrafPtr savePort;
  683.     Boolean saveWELock;
  684.  
  685.     // lock the WE record
  686.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  687.     pWE = *hWE;
  688.  
  689.     // set up the port
  690.     GetPort(&savePort);
  691.     SetPort(pWE->port);
  692.  
  693.     // make sure rangeStart comes before rangeEnd
  694.     _WEReorder(&rangeStart, &rangeEnd);
  695.  
  696.     // calculate pixel location corresponding to rangeStart
  697.     WEGetPoint(rangeStart, &firstPoint, &firstLineHeight, hWE);
  698.  
  699.     // calculate pixel location corresponding to rangeEnd
  700.     WEGetPoint(rangeEnd, &lastPoint, &lastLineHeight, hWE);
  701.  
  702.     // open a region: rects to be hilited will be accumulated in this
  703.     OpenRgn();
  704.  
  705.     if (firstPoint.v == lastPoint.v) 
  706.     {
  707.         // selection range encompasses only one line
  708.         WESetLongRect(&selRect, firstPoint.h, firstPoint.v, lastPoint.h, lastPoint.v + lastLineHeight);
  709.         WELongRectToRect(&selRect, &r);
  710.         FrameRect(&r);
  711.     }
  712.     else
  713.     {
  714.         // selection range encompasses more than one line
  715.         // hilite the first line
  716.         WESetLongRect(&selRect, firstPoint.h, firstPoint.v, pWE->destRect.right, firstPoint.v + firstLineHeight);
  717.         WELongRectToRect(&selRect, &r);
  718.         FrameRect(&r);
  719.  
  720.         // any lines between the first and the last one?
  721.         if (firstPoint.v + firstLineHeight < lastPoint.v) 
  722.         {
  723.             // hilite all the lines in-between
  724.             WESetLongRect(&selRect, pWE->destRect.left, firstPoint.v + firstLineHeight, pWE->destRect.right, lastPoint.v);
  725.             WELongRectToRect(&selRect, &r);
  726.             FrameRect(&r);
  727.         }
  728.  
  729.         // hilite the last line
  730.         WESetLongRect(&selRect, pWE->destRect.left, lastPoint.v, lastPoint.h, lastPoint.v + lastLineHeight);
  731.         WELongRectToRect(&selRect, &r);
  732.         FrameRect(&r);
  733.     }
  734.  
  735.     // copy the accumulated region into a new region
  736.     hiliteRgn = NewRgn();
  737.     CloseRgn(hiliteRgn);
  738.  
  739.     // restrict this region to the view region
  740.     SectRgn(hiliteRgn, pWE->viewRgn, hiliteRgn);
  741.  
  742.     // restore the port
  743.     SetPort(savePort);
  744.  
  745.     // unlock the WE record
  746.     _WESetHandleLock((Handle) hWE, saveWELock);
  747.  
  748.     // return the hilite region
  749.     return hiliteRgn;
  750. }
  751.  
  752. pascal void _WEHiliteRange(long rangeStart, long rangeEnd, WEHandle hWE)
  753. {
  754.     WEPtr pWE;
  755.     RgnHandle saveClip, auxRgn, hiliteRgn;
  756.     PenState savePen;
  757.     GrafPtr savePort;
  758.  
  759.     // the WE record must be already locked
  760.     pWE = *hWE;
  761.  
  762.     // do nothing if the specified range is empty
  763.     if (rangeStart == rangeEnd) 
  764.     {
  765.         return;
  766.     }
  767.  
  768.     // set up the port
  769.     GetPort(&savePort);
  770.     SetPort(pWE->port);
  771.  
  772.     // create auxiliary regions
  773.     saveClip = NewRgn();
  774.     auxRgn = NewRgn();
  775.  
  776.     // restrict the clip region to the view rectangle
  777.     GetClip(saveClip);
  778.     SectRgn(saveClip, pWE->viewRgn, auxRgn);
  779.     SetClip(auxRgn);
  780.  
  781.     // get the hilite region corresponding to the specified range
  782.     hiliteRgn = WEGetHiliteRgn(rangeStart, rangeEnd, hWE);
  783.  
  784.     // hilite the region or frame it, depending on the setting of the active flag
  785.     if (BTST(pWE->flags, weFActive))
  786.     {
  787.         _WEClearHiliteBit();
  788.         InvertRgn(hiliteRgn);
  789.     }
  790.     else if (BTST(pWE->features, weFOutlineHilite)) 
  791.     {
  792.         GetPenState(&savePen);
  793.         PenNormal();
  794.         PenMode(patXor);
  795.         _WEClearHiliteBit();
  796.         FrameRgn(hiliteRgn);
  797.         SetPenState(&savePen);
  798.     }
  799.  
  800.     // restore the clip region
  801.     SetClip(saveClip);
  802.  
  803.     // dispose of all regions
  804.     DisposeRgn(saveClip);
  805.     DisposeRgn(auxRgn);
  806.     DisposeRgn(hiliteRgn);
  807.  
  808.     // restore the port
  809.     SetPort(savePort);
  810. }
  811.  
  812. pascal void WESetSelection(long selStart, long selEnd, WEHandle hWE)
  813. {
  814.     WEPtr pWE;
  815.     long oldSelStart, oldSelEnd;
  816.     Boolean saveWELock;
  817.  
  818.     // lock the WE record
  819.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  820.     pWE = *hWE;
  821.     
  822.     // range-check parameters
  823.     selStart = _WEPinInRange(selStart, 0, pWE->textLength);
  824.     selEnd = _WEPinInRange(selEnd, 0, pWE->textLength);
  825.  
  826.     // set the weFAnchorIsEnd bit if selStart > selEnd,  reorder the endpoints
  827.     if (selStart > selEnd) 
  828.     {
  829.         BSET(pWE->flags, weFAnchorIsEnd);
  830.         _WEReorder(&selStart, &selEnd);    // only if there is a problem
  831.     }
  832.     else
  833.     {
  834.         BCLR(pWE->flags, weFAnchorIsEnd);
  835.     }
  836.  
  837.     // get old selection range
  838.     oldSelStart = pWE->selStart;
  839.     oldSelEnd = pWE->selEnd;
  840.  
  841.     // selection changed?
  842.     if (oldSelStart != selStart || oldSelEnd != selEnd)
  843.     {
  844.         // invalid the null style
  845.         BCLR(pWE->flags, weFUseNullStyle);
  846.  
  847.         // hide the caret if it's showing
  848.         if (BTST(pWE->flags, weFCaretVisible))
  849.         {
  850.             _WEBlinkCaret(hWE);
  851.         }
  852.  
  853.         // set new selection range
  854.         pWE->selStart = selStart;
  855.         pWE->selEnd = selEnd;
  856.  
  857.         // skip this section if redrawing has been inhibited
  858.         if (!BTST(pWE->features, weFInhibitRecal))
  859.         {
  860.             // if we're active, invert the exclusive-OR between the old range and the new range.
  861.             // if we're inactive, this optimization can't be used because of outline highlighting.
  862.             if (BTST(pWE->flags, weFActive))
  863.             { 
  864.                 _WEReorder(&oldSelStart, &selStart);
  865.                 _WEReorder(&oldSelEnd, &selEnd);
  866.                 _WEReorder(&oldSelEnd, &selStart);
  867.             }
  868.     
  869.             _WEHiliteRange(oldSelStart, oldSelEnd, hWE);
  870.             _WEHiliteRange(selStart, selEnd, hWE);
  871.     
  872.             if (!BTST(pWE->flags, weFMouseTracking))
  873.             {
  874.                 // redraw the caret immediately, if the selection range is empty
  875.                 if (pWE->selStart == pWE->selEnd)
  876.                 { 
  877.                     _WEBlinkCaret(hWE);
  878.                 }
  879.                 // clear clickCount, unless we're tracking the mouse
  880.                 pWE->clickCount = 0;
  881.     
  882.                 // scroll the selection into view, unless we're tracking the mouse
  883.                 WESelView(hWE);
  884.     
  885.             }
  886.         } // if redrawing not inhibited
  887.     } // if selection changed
  888.     
  889.     // unlock the WE record
  890.     _WESetHandleLock((Handle) hWE, saveWELock);
  891. }
  892.  
  893. pascal void WESetAlignment(char alignment, WEHandle hWE)
  894. {
  895.     WEPtr pWE;
  896.     short oldAlignment;
  897.     Boolean saveWELock;
  898.  
  899.     // lock the WE record
  900.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  901.     pWE = *hWE;
  902.     oldAlignment = pWE->alignment;
  903.  
  904.     if ((alignment >= weFlushLeft) && (alignment <= weJustify) && (alignment != oldAlignment))
  905.     {
  906.         // hide the caret if it's showing
  907.         if (BTST(pWE->flags, weFCaretVisible))
  908.             _WEBlinkCaret(hWE);
  909.  
  910.         // change the alignment
  911.         pWE->alignment = alignment;
  912.  
  913.         // if the text was left-aligned, then we haven't been bothering till now,
  914.         // so we have to recalc the whole document
  915.         if (oldAlignment == weFlushLeft)
  916.             _WERecalSlops(0, pWE->nLines - 1, hWE);
  917.  
  918.         // redraw the view rectangle
  919.         WEUpdate(NULL, hWE);
  920.     }
  921.     
  922.     // unlock the WE record
  923.     _WESetHandleLock((Handle) hWE, saveWELock);
  924. }
  925.  
  926. pascal long _WEArrowOffset(short action, long offset, WEHandle hWE)
  927. {
  928.     // given an action code (corresponding to a modifiers + arrow key combo)
  929.     // and an offset into the text, find the offset of the new caret position
  930.  
  931.     LongPt thePoint;
  932.     long textLength, rangeStart, rangeEnd;
  933.     short lineHeight;
  934.     char edge;
  935.  
  936.     textLength = (*hWE)->textLength;
  937.     switch (action)
  938.     {
  939.         case kGoLeft: 
  940.             if (offset > 0)
  941.             {
  942.                 offset--;
  943.                 if (WECharByte(offset, hWE) != smSingleByte) 
  944.                 {
  945.                     offset--;
  946.                 }
  947.             }
  948.             break;
  949.             
  950.         case kGoRight: 
  951.             if (offset < textLength)
  952.             {
  953.                 if (WECharByte(offset, hWE) != smSingleByte) 
  954.                 {
  955.                     offset++;
  956.                 }
  957.                 offset++;
  958.             }
  959.             break;
  960.  
  961.         case kGoUp: 
  962.             WEGetPoint(offset, &thePoint, &lineHeight, hWE);
  963.             thePoint.v--;
  964.             offset = WEGetOffset(&thePoint, &edge, hWE);
  965.             break;
  966.  
  967.         case kGoDown: 
  968.             WEGetPoint(offset, &thePoint, &lineHeight, hWE);
  969.             thePoint.v += lineHeight;
  970.             offset = WEGetOffset(&thePoint, &edge, hWE);
  971.             break;
  972.             
  973.         case kGoWordStart: 
  974.             WEFindWord(offset, kTrailingEdge, &rangeStart, &rangeEnd, hWE);
  975.             offset = rangeStart;
  976.             break;
  977.             
  978.         case kGoWordEnd: 
  979.             WEFindWord(offset, kLeadingEdge, &rangeStart, &rangeEnd, hWE);
  980.             offset = rangeEnd;
  981.             break;
  982.             
  983.         case kGoTextStart: 
  984.             offset = 0;
  985.             break;
  986.  
  987.         case kGoTextEnd: 
  988.             offset = textLength;
  989.             break;
  990.  
  991.         case kGoLineStart: 
  992.             WEFindLine(offset, kLeadingEdge, &rangeStart, &rangeEnd, hWE);
  993.             offset = rangeStart;
  994.             break;
  995.  
  996.         case kGoLineEnd: 
  997.             WEFindLine(offset, kTrailingEdge, &rangeStart, &rangeEnd, hWE);
  998.             offset = rangeEnd;
  999.             if (offset < textLength) 
  1000.             {
  1001.                 offset--;
  1002.                 if (WECharByte(offset, hWE) != smSingleByte) 
  1003.                 {
  1004.                     offset--;
  1005.                 }
  1006.             }
  1007.             break;
  1008.             
  1009.         default:
  1010.             break;
  1011.     }
  1012.  
  1013.     return offset;
  1014. }
  1015.  
  1016. pascal void _WEDoArrowKey (short arrow, EventModifiers modifiers, WEHandle hWE)
  1017. {
  1018.     // this routine is called by WEKey to handle arrow keys
  1019.  
  1020.     WEPtr pWE = *hWE;    // assume the WE record is already locked
  1021.     short action;
  1022.     long selStart, selEnd;
  1023.     long caretLoc, anchor;
  1024.  
  1025.     // calculate the "action" parameter for _WEArrowOffset from arrow and modifiers
  1026.     action = arrow - kArrowLeft;            // possible range: 0..3
  1027.     if (modifiers & optionKey)
  1028.     {
  1029.         action += kOption;
  1030.     }
  1031.     if (modifiers & cmdKey)
  1032.     {
  1033.         action += kCommand;
  1034.     }
  1035.     
  1036.     // get selection range
  1037.     selStart = pWE->selStart;
  1038.     selEnd = pWE->selEnd;
  1039.  
  1040.     if ((modifiers & shiftKey) == 0) 
  1041.     {
  1042.         // if selection range isn't empty, collapse it to one of the endpoints
  1043.         if (selStart < selEnd) 
  1044.         {
  1045.             if ((arrow == kArrowLeft) || (arrow == kArrowUp)) 
  1046.             {
  1047.                 caretLoc = selStart;
  1048.             }
  1049.             else
  1050.             {
  1051.                 caretLoc = selEnd;
  1052.             }
  1053.         }
  1054.         else
  1055.         {
  1056.             // otherwise move the insertion point
  1057.             caretLoc = _WEArrowOffset(action, selStart, hWE);
  1058.         }
  1059.         
  1060.         // set anchor to caretLoc, so new selection will be empty
  1061.         anchor = caretLoc;
  1062.     }
  1063.     else
  1064.     {
  1065.         // shift key was held down: extend the selection rather than replacing it
  1066.         // find out which selection boundary is the anchor and which is the free endpoint
  1067.         if (BTST(pWE->flags, weFAnchorIsEnd)) 
  1068.         {
  1069.             anchor = selEnd;
  1070.             caretLoc = selStart;
  1071.         }
  1072.         else
  1073.         {
  1074.             anchor = selStart;
  1075.             caretLoc = selEnd;
  1076.         }
  1077.         
  1078.         // move the free endpoint
  1079.         caretLoc = _WEArrowOffset(action, caretLoc, hWE);
  1080.     }
  1081.  
  1082.     // select the new selection
  1083.     WESetSelection(anchor, caretLoc, hWE);
  1084. }
  1085.  
  1086. pascal Boolean WEAdjustCursor(Point mouseLoc, RgnHandle mouseRgn, WEHandle hWE)
  1087. {
  1088.     // Call WEAdjustCursor to set the cursor shape when the mouse is in the view rectangle.
  1089.     // MouseRgn should be either a valid region handle or NULL.
  1090.     // If mouseRgn is supplied (i.e., if it's not NULL), it is intersected with a region
  1091.     // in global coordinates within which the cursor is to retain its shape.
  1092.     // WEAdjustCursor returns TRUE if the cursor has been set.
  1093.     // Your application should set the cursor only if WEAdjustCursor returns FALSE.
  1094.  
  1095.     WEPtr pWE;
  1096.     RgnHandle auxRgn, hiliteRgn;
  1097.     enum { kIBeam, kArrow} cursorType;
  1098.     Point portDelta;
  1099.     GrafPtr savePort;
  1100.     Boolean saveWELock;
  1101.     Boolean adjustCursor;
  1102.  
  1103.     adjustCursor = false;
  1104.     cursorType = kIBeam;
  1105.  
  1106.     // lock the WE record
  1107.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1108.     pWE = *hWE;
  1109.  
  1110.     // set up the port
  1111.     GetPort(&savePort);
  1112.     SetPort(pWE->port);
  1113.  
  1114.     // calculate delta between the local coordinate system and the global one
  1115.     portDelta.v = 0;
  1116.     portDelta.h = 0;
  1117.     LocalToGlobal(&portDelta);
  1118.  
  1119.     // calculate the visible portion of the view rectangle, in global coordinates
  1120.     auxRgn = NewRgn();
  1121.     CopyRgn(pWE->viewRgn, auxRgn);
  1122.     SectRgn(auxRgn, pWE->port->visRgn, auxRgn);
  1123.     OffsetRgn(auxRgn, portDelta.h, portDelta.v);
  1124.  
  1125.     if (PtInRgn(mouseLoc, auxRgn)) 
  1126.     {
  1127.         // mouse is within view rectangle: it's up to us to set the cursor
  1128.         adjustCursor = true;
  1129.  
  1130.         // if drag-and-drop is enabled, see if the mouse is within current selection
  1131.         if (BTST(pWE->flags, weFHasDragManager) && BTST(pWE->features, weFDragAndDrop))
  1132.         {
  1133.             if (pWE->selStart < pWE->selEnd)
  1134.             {
  1135.             
  1136.                 // get current hilite region in global coordinates
  1137.                 hiliteRgn = WEGetHiliteRgn(pWE->selStart, pWE->selEnd, hWE);
  1138.                 OffsetRgn(hiliteRgn, portDelta.h, portDelta.v);
  1139.  
  1140.                 // if mouse is within selection, set cursor to an arrow, else to an I-beam
  1141.                 // (actually, we still use an I-beam if less than DoubleTime ticks have elapsed
  1142.                 // since the last mouse click, so that the cursor doesn't turn into an arrow while
  1143.                 // triple-clicking + dragging a range of lines)
  1144.  
  1145.                 if (PtInRgn(mouseLoc, hiliteRgn) && ((TickCount() > pWE->clickTime + GetDblTime()) ||
  1146.                     (pWE->clickEdge == kObjectEdge)))
  1147.                 {
  1148.                     cursorType = kArrow;                // use arrow cursor
  1149.                     CopyRgn(hiliteRgn, auxRgn);
  1150.                 }
  1151.                 else
  1152.                 {
  1153.                     DiffRgn(auxRgn, hiliteRgn, auxRgn);
  1154.                 }
  1155.                 
  1156.                 // dispose of the hilite region
  1157.                 DisposeRgn(hiliteRgn);
  1158.  
  1159.             } // if drag-and-drop is enabled
  1160.         }
  1161.         
  1162.         // set the cursor
  1163.         if (cursorType == kIBeam)
  1164.             SetCursor(*GetCursor(iBeamCursor));
  1165.         else
  1166.             SetCursor(&qd.arrow);
  1167.  
  1168.         // set mouseRgn, if provided
  1169.         if (mouseRgn != NULL) 
  1170.         {
  1171.             SectRgn(mouseRgn, auxRgn, mouseRgn);
  1172.         }
  1173.     }
  1174.     else
  1175.     {
  1176.         // mouse is outside view rectangle: don't set the cursor; subtract viewRgn from mouseRgn
  1177.         if (mouseRgn != NULL) 
  1178.         {
  1179.             DiffRgn(mouseRgn, auxRgn, mouseRgn);
  1180.         }
  1181.     }
  1182.     // dispose of the temporary region
  1183.     DisposeRgn(auxRgn);
  1184.  
  1185.     // restore the port
  1186.     SetPort(savePort);
  1187.  
  1188.     // unlock the WE record
  1189.     _WESetHandleLock((Handle) hWE, saveWELock);
  1190.     
  1191.     return adjustCursor;
  1192. }
  1193.  
  1194. pascal void WEIdle(unsigned long *maxSleep, WEHandle hWE)
  1195. {
  1196.     WEPtr pWE;
  1197.     unsigned long currentTime, blinkTime, sleep;
  1198.     Boolean saveWELock;
  1199.  
  1200.     // lock the WE record
  1201.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1202.     pWE = *hWE;
  1203.  
  1204. #if WASTE_DEBUG
  1205.         _WESanityCheck(hWE);
  1206. #endif
  1207.  
  1208.     // the caret blinks only if we're active and the selection point is empty
  1209.     if (BTST(pWE->flags, weFActive) && (pWE->selStart == pWE->selEnd)) 
  1210.     {
  1211.         // get current time
  1212.         currentTime = TickCount();
  1213.         
  1214.         // calculate when the caret should be blinked again
  1215.         blinkTime = pWE->caretTime + GetCaretTime();
  1216.  
  1217.         if (currentTime < blinkTime)
  1218.         {
  1219.             sleep = blinkTime - currentTime;
  1220.         }
  1221.         else
  1222.         {
  1223.             _WEBlinkCaret(hWE);
  1224.             sleep = GetCaretTime();
  1225.         }
  1226.     }
  1227.     else
  1228.     {
  1229.         // if we don't need to blink the caret, we can sleep forever
  1230.         sleep = LONG_MAX;
  1231.     }
  1232.     
  1233.     // return sleepTime to the caller if maxSleep isn't NULL
  1234.     if (maxSleep != NULL)
  1235.     {
  1236.         *maxSleep = sleep;
  1237.     }
  1238.     
  1239.     // unlock the WE record
  1240.     _WESetHandleLock((Handle) hWE, saveWELock);
  1241. }
  1242.  
  1243. pascal void WEUpdate(RgnHandle updateRgn, WEHandle hWE)
  1244. {
  1245.     WEPtr pWE;
  1246.     long firstLine, lastLine;
  1247.     Rect auxRect;
  1248.     RgnHandle saveClip, auxRgn;
  1249.     GrafPtr savePort;
  1250.     Boolean saveWELock;
  1251.  
  1252.     // lock the WE record
  1253.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1254.     pWE = *hWE;
  1255.  
  1256.     // set up the port
  1257.     GetPort(&savePort);
  1258.     SetPort(pWE->port);
  1259.  
  1260.     // save the clip region
  1261.     saveClip = NewRgn();
  1262.     GetClip(saveClip);
  1263.  
  1264.     // clip to the insersection between updateRgn and the view rectangle
  1265.     // (updateRgn may be NULL; in this case, just clip to the view rectangle)
  1266.     auxRgn = NewRgn();
  1267.     if (updateRgn != NULL) 
  1268.     {
  1269.         SectRgn(updateRgn, pWE->viewRgn, auxRgn);
  1270.     }
  1271.     else
  1272.     {
  1273.         CopyRgn(pWE->viewRgn, auxRgn);
  1274.     }
  1275.     SetClip(auxRgn);
  1276.  
  1277.     if (EmptyRgn(auxRgn) == false) 
  1278.     {
  1279.         // set auxRect to the bounding box of the update region (clipped to the view rectangle)
  1280.         auxRect = (*auxRgn)->rgnBBox;
  1281.  
  1282.         // find out which lines need to be redrawn
  1283.         firstLine = _WEPixelToLine(auxRect.top - pWE->destRect.top, hWE);
  1284.         lastLine = _WEPixelToLine((auxRect.bottom - 1) - pWE->destRect.top, hWE);
  1285.  
  1286.         // draw them (if updateRgn is NULL, erase each line rectangle before redrawing)
  1287.         _WEDrawLines(firstLine, lastLine, (updateRgn == NULL), hWE);
  1288.  
  1289.         // hilite the selection range or draw the caret (only if active)
  1290.         if (pWE->selStart < pWE->selEnd) 
  1291.             _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1292.         else if (BTST(pWE->flags, weFCaretVisible)) 
  1293.         {
  1294.             _WEBlinkCaret(hWE);
  1295.             BSET(pWE->flags, weFCaretVisible);
  1296.         }
  1297.     }
  1298.  
  1299.     DisposeRgn(auxRgn);
  1300.  
  1301.     // restore the clip region
  1302.     SetClip(saveClip);
  1303.     DisposeRgn(saveClip);
  1304.  
  1305.     // restore the port
  1306.     SetPort(savePort);
  1307.  
  1308.     // unlock the WE record
  1309.     _WESetHandleLock((Handle) hWE, saveWELock);
  1310. }
  1311.  
  1312. pascal void WEDeactivate(WEHandle hWE)
  1313. {
  1314.     WEPtr pWE;
  1315.     Boolean saveWELock;
  1316.  
  1317.     // lock the WE record
  1318.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1319.     pWE = *hWE;
  1320.  
  1321.     // do nothing if we are already inactive
  1322.     if (BTST(pWE->flags, weFActive))
  1323.     {
  1324.  
  1325.         // hide the selection range or the caret
  1326.         _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1327.         if (BTST(pWE->flags, weFCaretVisible))
  1328.             _WEBlinkCaret(hWE);
  1329.  
  1330.         // clear the active flag
  1331.         BCLR(pWE->flags, weFActive);
  1332.  
  1333.         // frame the selection
  1334.         _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1335.  
  1336.         // dispose of the offscreen graphics world, if any
  1337.         if (pWE->offscreenPort != NULL)
  1338.         {
  1339.             DisposeGWorld((GWorldPtr)(pWE->offscreenPort));
  1340.             pWE->offscreenPort = NULL;
  1341.         }
  1342.  
  1343.         // notify Text Services
  1344.         if (pWE->tsmReference != NULL)
  1345.             DeactivateTSMDocument(pWE->tsmReference);
  1346.     }
  1347.     
  1348.     // unlock the WE record
  1349.     _WESetHandleLock((Handle) hWE, saveWELock);
  1350. }
  1351.  
  1352. pascal void WEActivate(WEHandle hWE)
  1353. {
  1354.     WEPtr pWE;
  1355.     Boolean saveWELock;
  1356.  
  1357.     if (WEIsActive(hWE)) return;
  1358.  
  1359.     // lock the WE record
  1360.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1361.     pWE = *hWE;
  1362.  
  1363.     // remove the selection frame
  1364.     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1365.  
  1366.     // set the active flag
  1367.     BSET(pWE->flags, weFActive);
  1368.  
  1369.     // show the selection range
  1370.     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1371.  
  1372.     // notify Text Services
  1373.     if (pWE->tsmReference != NULL) 
  1374.     {
  1375.         ActivateTSMDocument(pWE->tsmReference);
  1376.     }
  1377.     
  1378.     // unlock the WE record
  1379.     _WESetHandleLock((Handle) hWE, saveWELock);
  1380. }
  1381.  
  1382. pascal Boolean WEIsActive(WEHandle hWE)
  1383. {
  1384.     // return TRUE iff the specified WE instance is currently active
  1385.     return BTST((*hWE)->flags, weFActive) ? true : false;
  1386. }
  1387.  
  1388. pascal void WEScroll(long hOffset, long vOffset, WEHandle hWE)
  1389. {
  1390.     WEPtr pWE;
  1391.     Rect viewRect;
  1392.     RgnHandle updateRgn;
  1393.     GrafPtr savePort;
  1394.     Boolean hideOutline, saveWELock;
  1395.  
  1396.     // do nothing if both scroll offsets are zero
  1397.     if ((hOffset == 0) && (vOffset == 0))
  1398.         return;
  1399.  
  1400.     // lock the WE record
  1401.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1402.     pWE = *hWE;
  1403.  
  1404.     // hide the caret if it's showing
  1405.     if (BTST(pWE->flags, weFCaretVisible))
  1406.     {
  1407.         _WEBlinkCaret(hWE);
  1408.     }
  1409.     
  1410. #if WASTE_PIN_SCROLL
  1411.     // CKT Sep 12 94 Begin - Added PinScroll behavior
  1412.     if(vOffset > 0){
  1413.         // if top of the destRect would be moved below top of the viewRect
  1414.         if(pWE->destRect.top + vOffset > pWE->viewRect.top){
  1415.             vOffset += -((pWE->destRect.top + vOffset) - pWE->viewRect.top);
  1416.         }
  1417.     }else if(vOffset < 0){
  1418.         //if bottom of the destRect would be moved above bottom of the viewRect
  1419.         if(pWE->destRect.bottom + vOffset < pWE->viewRect.bottom){
  1420.             vOffset +=  -((pWE->destRect.bottom + vOffset) - pWE->viewRect.bottom);
  1421.         }
  1422.     }
  1423.     // CKT Sep 12 94 End
  1424. #endif
  1425.  
  1426.     // set up the port
  1427.     GetPort(&savePort);
  1428.     SetPort(pWE->port);
  1429.  
  1430.     // if we're inactive and outline highlighting is on, we have to temporarily
  1431.     // hide the selection outline while scrolling to avoid a cosmetic bug
  1432.     hideOutline = false;
  1433.     if (!BTST(pWE->flags, weFActive))
  1434.         if (BTST(pWE->features, weFOutlineHilite))
  1435.         {
  1436.             hideOutline = true;
  1437.             _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1438.             BCLR(pWE->features, weFOutlineHilite);
  1439.         }
  1440.  
  1441.     // if we're currently tracking a drag, notify the Drag Manager we're about to scroll
  1442.     if (pWE->currentDrag != kNullDrag)
  1443.         DragPreScroll(pWE->currentDrag, hOffset, vOffset);
  1444.     
  1445.     viewRect = (*pWE->viewRgn)->rgnBBox;
  1446.     updateRgn = NewRgn();
  1447.  
  1448.     // offset the destination rectangle by the specified amount
  1449.     WEOffsetLongRect(&pWE->destRect, hOffset, vOffset);
  1450.  
  1451.     // scroll the view rectangle
  1452.     ScrollRect(&viewRect, hOffset, vOffset, updateRgn);
  1453.  
  1454.     // redraw the exposed region
  1455.     WEUpdate(updateRgn, hWE);
  1456.     DisposeRgn(updateRgn);
  1457.  
  1458.     // notify the Drag Manager
  1459.     if (pWE->currentDrag != kNullDrag)
  1460.         DragPostScroll(pWE->currentDrag);
  1461.  
  1462.     // redraw the selection outline, if hidden
  1463.     if (hideOutline)
  1464.     {
  1465.         BSET(pWE->features, weFOutlineHilite);
  1466.         _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  1467.     }
  1468.  
  1469.     // restore the port
  1470.     SetPort(savePort);
  1471.  
  1472.     // unlock the WE record
  1473.     _WESetHandleLock((Handle) hWE, saveWELock);
  1474. }
  1475.  
  1476. pascal Boolean _WEScrollIntoView (long offset, WEHandle hWE)
  1477. {
  1478.     WEPtr pWE = *hWE;
  1479.     LongPt thePoint;
  1480.     short lineHeight;
  1481.     long hScroll, vScroll, temp;
  1482.     Boolean retval;
  1483.  
  1484.     // do nothing if automatic scrolling is disabled
  1485.     if (!BTST(pWE->features, weFAutoScroll)) 
  1486.     {
  1487.         return false;
  1488.     }
  1489.  
  1490.     // find the selection point
  1491.     WEGetPoint(offset, &thePoint, &lineHeight, hWE);
  1492.  
  1493.     // assume no scrolling is needed
  1494.     retval = false;
  1495.     vScroll = 0;
  1496.     hScroll = 0;
  1497.  
  1498.     // determine if we need to scroll vertically
  1499.     if ((thePoint.v < pWE->viewRect.top) || 
  1500.         (thePoint.v + lineHeight >= pWE->viewRect.bottom))
  1501.     {
  1502.         // calculate the amount of vertical scrolling needed to center the selection into view
  1503.         vScroll = ((pWE->viewRect.top + pWE->viewRect.bottom) >> 1) -
  1504.                     (thePoint.v + (lineHeight >> 1));
  1505.  
  1506.         // we'd like to superimpose the bottom margins of the dest/view rects, if possible
  1507.         temp = pWE->viewRect.bottom - pWE->destRect.bottom;
  1508.         if (temp > vScroll) 
  1509.         {
  1510.             vScroll = temp;
  1511.         }
  1512.         // but we also have to make sure the dest top isn't scrolled below the view top
  1513.         temp = pWE->viewRect.top - pWE->destRect.top;
  1514.         if (temp < vScroll) 
  1515.         {
  1516.             vScroll = temp;
  1517.         }
  1518.     }
  1519.     
  1520.     // determine if we need to scroll horizontally
  1521.     if ((thePoint.h - 1 < pWE->viewRect.left) || (thePoint.h >= pWE->viewRect.right))
  1522.     { 
  1523.         // calculate the amount of horizontal scrolling needed to center the selection into view
  1524.         hScroll = ((pWE->viewRect.left + pWE->viewRect.right) >> 1) - thePoint.h;
  1525.  
  1526.         // we'd like to superimpose the right margins of the dest/view rects, if possible
  1527.         temp = pWE->viewRect.right - pWE->destRect.right;
  1528.         if (temp > hScroll) 
  1529.         {
  1530.             hScroll = temp;
  1531.         }
  1532.         
  1533.         // but we also have to make sure the dest left isn't scrolled to the right of the view left
  1534.         temp = pWE->viewRect.left - pWE->destRect.left;
  1535.         if (temp < hScroll) 
  1536.         {
  1537.             hScroll = temp;
  1538.         }
  1539.     }
  1540.     
  1541.     // scroll the text if necessary
  1542.     if ((vScroll != 0) || (hScroll != 0)) 
  1543.     {
  1544.         retval = true;
  1545.         WEScroll(hScroll, vScroll, hWE);
  1546.         BSET(pWE->flags, weFDestRectChanged);
  1547.     }
  1548.  
  1549.     // notify our client of changes to the destination rectangle
  1550.     if (BTST(pWE->flags, weFDestRectChanged))
  1551.     {
  1552.         if (pWE->scrollProc != NULL) 
  1553.         {
  1554.             CallWEScrollProc(hWE, pWE->scrollProc);
  1555.         }
  1556.         BCLR(pWE->flags, weFDestRectChanged);
  1557.     }
  1558.  
  1559.     // call the scroll callback, if any
  1560.     return retval;
  1561. }
  1562.  
  1563. pascal void WESelView(WEHandle hWE)
  1564. {
  1565.     WEPtr pWE;
  1566.     Boolean saveWELock;
  1567.  
  1568.     // lock the WE record
  1569.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  1570.     pWE = *hWE;
  1571.  
  1572.     // scroll the free endpoint of the selection into view
  1573.     _WEScrollIntoView(BTST(pWE->flags, weFAnchorIsEnd) ? pWE->selStart : pWE->selEnd, hWE);
  1574.  
  1575.     // unlock the WE record
  1576.     _WESetHandleLock((Handle) hWE, saveWELock);
  1577. }
  1578.